<template>
  <div class="api">
    <div class="apiLeft">
      <div>
        <el-input ref="input" v-model="apiNameKeyword" placeholder="模块搜索" @keyup.native="goSearch2" @keydown.native.enter="searchApiName">
          <el-button slot="append" icon="el-icon-search" @click="searchApiName" />
        </el-input>
      </div>
      <div class="apiMenu">
        <el-menu
          default-active="0_0_0"
          class="el-menu-vertical-demo"
        >
          <el-submenu v-for="(gruop,index) in apiList" :key="index" :ref="'_'+index.toString()" :index="index.toString()">
            <template slot="title">
              <span>{{ gruop.groupName }}</span>
            </template>
            <el-menu-item-group>
              <el-submenu v-for="(apiGroup,index2) in gruop.apiList" :key="index2" :ref="'_'+index+'_'+index2" :index="index+'_'+index2">
                <template slot="title">{{ apiGroup.apiName }}</template>
                <el-menu-item v-for="(apiModel,index3) in apiGroup.apiList" :key="index3" :ref="'_'+index+'_'+index2+'_'+index3" :index="index+'_'+index2+'_'+index3" @click="toView(apiModel)">
                  <el-tag size="small" :style="'margin-right: 5px;color: #ffffff;'+getSpanTitleColor(apiModel.method)">{{ apiModel.method }}</el-tag>
                  <el-tooltip placement="left" effect="light">
                    <div slot="content">
                      {{ apiModel.apiName }}
                    </div>
                    <a class="apiName">{{ apiModel.apiName }}</a>
                  </el-tooltip>
                </el-menu-item>
              </el-submenu>
            </el-menu-item-group>
          </el-submenu>
        </el-menu>
      </div>
    </div>
    <div class="apiRight">
      <div>
        <el-input ref="input" v-model="inputValue" placeholder="api搜索" @keyup.native="goSearch" @keydown.native.enter="search">
          <el-button slot="append" icon="el-icon-search" @click="search" />
        </el-input>
      </div>
      <div class="apiDocument">
        <el-tabs v-model="editableTabsValue" type="card" closable @tab-remove="removeTab">
          <el-tab-pane
            v-for="(apiG, index) in apiTabs"
            :key="index"
            :label="apiG.api.apiName"
            :name="index.toString()"
          >
            <div class="tabCard">
              <el-tabs type="border-card" value="first">
                <el-tab-pane label="文档" name="first">
                  <div class="apiDocument">
                    <el-descriptions :title="apiG.api.apiName" direction="vertical" :column="3" border>
                      <el-descriptions-item label="接口地址" :span="3">{{ apiG.api.path }}</el-descriptions-item>
                      <el-descriptions-item label="请求方式" :span="1">{{ apiG.api.method }}</el-descriptions-item>
                      <el-descriptions-item label="请求类型" :span="2">{{ getRequestType(apiG.api.requestType,apiG.api.method) }}</el-descriptions-item>
                    </el-descriptions>
                    <div>
                      <span><h4>请求参数</h4></span>
                    </div>
                    <div>
                      <el-button type="primary" round size="mini" style="margin-bottom: 10px" @click="copyParam(apiG.api.requestParamsList)">点击复制参数对象</el-button>
                    </div>
                    <div style="width: 100%">
                      <el-table
                        :data="apiG.api.requestParamsList"
                        style="width: 100%;margin-bottom: 20px;"
                        border
                        row-key="id"
                        default-expand-all
                        :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
                      >
                        <el-table-column
                          prop="name"
                          label="参数名称"
                          width="300"
                        />
                        <el-table-column
                          label="是否必须"
                          width="150"
                        >
                          <template slot-scope="scope">
                            <span v-if="scope.row.required" style="color: red">是</span>
                            <span v-else />
                          </template>
                        </el-table-column>
                        <el-table-column
                          label="数组"
                          width="150"
                        >
                          <template slot-scope="scope">
                            <span v-if="scope.row.array" style="color: red">array</span>
                            <span v-else />
                          </template>
                        </el-table-column>
                        <el-table-column
                          label="数据类型"
                          width="300"
                        >
                          <template slot-scope="scope">
                            <span> {{ getType(scope.row.dataType) }} </span>
                          </template>
                        </el-table-column>
                        <el-table-column
                          prop="describe"
                          label="参数说明"
                        />
                      </el-table>
                    </div>
                    <span><h4>响应参数</h4></span>
                    <div style="width: 100%">
                      <el-table
                        :data="apiG.api.responseParamsList"
                        style="width: 100%;margin-bottom: 20px;"
                        border
                        row-key="id"
                        default-expand-all
                        :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
                      >
                        <el-table-column
                          prop="name"
                          label="参数名称"
                          width="300"
                        />
                        <el-table-column
                          label="数组"
                          width="150"
                        >
                          <template slot-scope="scope">
                            <span v-if="scope.row.array" style="color: red">array</span>
                            <span v-else />
                          </template>
                        </el-table-column>
                        <el-table-column
                          label="数据类型"
                          width="300"
                        >
                          <template slot-scope="scope">
                            <span> {{ getType(scope.row.dataType) }} </span>
                          </template>
                        </el-table-column>
                        <el-table-column
                          prop="describe"
                          label="参数说明"
                        />
                      </el-table>
                    </div>
                  </div>
                </el-tab-pane>
                <el-tab-pane label="调试" name="second">
                  <div class="debug">
                    <div class="labelRight">
                      <div>
                        <span class="spanTitle" :style="getSpanTitleColor(apiG.debugApi.method)">{{ apiG.debugApi.method }}</span>
                      </div>
                      <div style="width: 80%">
                        <el-input v-model="apiG.debugApi.path" />
                      </div>
                      <div style="margin: 5px">
                        <el-dropdown class="el-dropdown" split-button type="primary" @click="sendRequest(apiG)">
                          发送请求
                          <el-dropdown-menu slot="dropdown">
                            <el-dropdown-item @click.native="openFileNameDialog(apiG)">下载文件</el-dropdown-item>
                          </el-dropdown-menu>
                        </el-dropdown>
                      </div>
                    </div>
                    <el-divider />
                    <span v-if="apiG.debugApi.requestType !== 'RESTFUL'"><h5>请求参数</h5></span>
                    <div v-if="apiG.fileData.length !== 0">
                      <el-tag type="danger">所有文件都移到这，支持选择文件，进行表单提交</el-tag>
                      <div>
                        <el-table
                          :data="apiG.fileData"
                          style="width: 100%"
                        >
                          <el-table-column
                            prop="name"
                            label="参数名称"
                            width="250"
                          />
                          <el-table-column
                            label="文件选择"
                            width="180"
                          >
                            <template slot-scope="scope">
                              <input type="file" @change="triggerFile($event, scope.row)">
                            </template>
                          </el-table-column>
                          <el-table-column
                            prop="describe"
                            label="参数说明"
                          />
                          <el-table-column
                            label="操作"
                            width="180"
                          >
                            <template slot-scope="scope">
                              <el-button v-if="scope.row.array" type="primary" size="small" icon="el-icon-plus" circle @click="addFile(apiG.fileData,scope.row)" />
                              <el-button type="danger" size="small" icon="el-icon-delete" circle @click="deleteFile(apiG.fileData,scope.$index,scope.row.array)" />
                            </template>
                          </el-table-column>
                        </el-table>
                      </div>
                    </div>
                    <div class="editor-container">
                      <VueJsonEditor v-model="apiG.requestParams" :height="240" />
                    </div>
                    <el-divider />
                    <span><h5>响应参数</h5></span>
                    <div class="editor-container">
                      <VueJsonEditor v-model="apiG.data" :height="240" />
                    </div>
                  </div>
                </el-tab-pane>
              </el-tabs>
            </div>
          </el-tab-pane>
        </el-tabs>
      </div>
    </div>
    <div>
      <el-dialog
        title="请输入文件名称"
        :visible.sync="dialogVisible"
        :close-on-click-modal="false"
        width="30%"
      >
        <el-input v-model="inputFileName" placeholder="请输入文件名称" />
        <span slot="footer" class="dialog-footer">
          <el-button @click="dialogVisible = false">取 消</el-button>
          <el-button type="primary" @click.native="sendRequest(apiG, 'blob')">确 定</el-button>
        </span>
      </el-dialog>
    </div>
  </div>
</template>

<script>
import { getApi } from '@/api/api'
import { getType } from '@/utils/typeUtil'
import { getRequestType } from '@/utils/requestType'
import jsonUtil from '@/utils/JsonUtil'
import formDataUtil from '@/utils/formDataUtil'
import { formDataRequest, getRequest, jsonRequest, restfulRequest } from '@/api/apiRequest'
import StringUtils from '@/utils/error/StringUtils'
import ErrorAssert from '@/utils/error/ErrorAssert'
import { formatDate } from '@/utils/dateUtil'
import MimeTypeEnum from '@/utils/MimeTypeEnum'
import VueJsonEditor from '@/components/VueJsonEditor'
export default {
  name: 'Index',
  components: { VueJsonEditor },
  data() {
    return {
      apiTabs: [
      ],
      a: {
        msg: '1111111'
      },
      apiG: null,
      inputFileName: '',
      inputValue: '',
      apiNameKeyword: '',
      dialogVisible: false,
      isShow: false,
      isSearch: false,
      status: true,
      status2: true,
      searchVal: '',
      hasChildren: true,
      apiList: [],
      list: [],
      dataList: [],
      searchList: [],
      editableTabsValue: 0
    }
  },
  mounted() {
    this.getApi()
  },
  methods: {
    copyParam(param) {
      // console.log(param)
      // param.forEach(p => {
      //   console.log(p.name)
      // })
      this.handelCopyLink(JSON.stringify(param))
    },
    handelCopyLink(val) {
      this.link = val
      const that = this
      this.$copyText(this.link).then(
        function(e) {
          that.$notify({
            title: '参数复制成功',
            type: 'success'
          })
        },
        function(e) {
          that.$notify({
            title: '参数复制失败',
            type: 'error'
          })
        }
      )
    },
    openFileNameDialog(apiG) {
      this.dialogVisible = true
      const date = new Date()
      const dt = formatDate(date, 'yyyyMMddhhmmss')
      this.inputFileName = `swagger_export_${dt}`
      this.apiG = apiG
    },
    getApi() {
      getApi().then((res) => {
        const { data } = res
        if (data.code === 0) {
          this.apiList = JSON.parse(JSON.stringify(data.data))
          this.list = JSON.parse(JSON.stringify(data.data))
        }
      })
    },
    getType(str) {
      return getType(str)
    },
    getRequestType(str, method) {
      if (method === 'GET') {
        return 'application/www-form-urlencoded'
      }
      return getRequestType(str)
    },
    getSpanTitleColor(str) {
      switch (str) {
        case 'GET':
          return 'background-color: #84C1FF;'
        case 'POST':
          return 'background-color: #67C23A'
        case 'PUT':
          return 'background-color: #E6A23C'
        case 'DELETE':
          return 'background-color: #F56C6C'
        default:
          return ''
      }
    },
    toView(api) {
      const _index = this.apiTabs.findIndex(at => {
        return at.id === api.id
      })
      if (_index === -1) {
        const fileDataList = []
        if (!StringUtils.isEmptyArray(api.requestParamsList)) {
          api.requestParamsList.forEach(param => {
            const { id, name, array, required, dataType, describe } = param
            const paramType = getType(dataType)
            if (paramType === 'file') {
              const fileDataParam = { id, name, array, describe, file: '', required }
              fileDataList.push(fileDataParam)
            }
          })
        }
        this.apiTabs.push({ id: api.id, api: api, debugApi: JSON.parse(JSON.stringify(api)), fileData: fileDataList, requestParams: jsonUtil.getJson(api.requestParamsList), data: {}})
        this.editableTabsValue = (this.apiTabs.length - 1).toString()
      } else {
        this.editableTabsValue = _index.toString()
      }
    },
    // 节流函数
    goSearch() {
      // 保持this的指向始终指向vue实例
      const that = this
      if (!that.status) {
        return
      }
      that.status = false
      setTimeout(function() {
        that.search() // 调用写好的方法
        that.status = true
      }, 1000)
    },
    // 节流函数
    goSearch2() {
      // 保持this的指向始终指向vue实例
      const that = this
      if (!that.status2) {
        return
      }
      that.status2 = false
      setTimeout(function() {
        that.searchApiName() // 调用写好的方法
        that.status2 = true
      }, 1000)
    },
    search() {
      this.apiList = []
      // 判断展示列表，如果输入了就展示没输入就不展示
      if (this.inputValue.length === 0) {
        this.apiList = JSON.parse(JSON.stringify(this.list))
        return
      }
      this.list.forEach(view => {
        const modelApi = []
        view.apiList.forEach(apiModelView => {
          const api = []
          apiModelView.apiList.forEach(apiView => {
            const path = apiView.path
            const apiName = apiView.apiName
            if (path.indexOf(this.inputValue) !== -1 || apiName.indexOf(this.inputValue) !== -1) {
              api.push(apiView)
            }
          })
          const modelView = JSON.parse(JSON.stringify(apiModelView))
          modelView.apiList = api
          if (api.length !== 0) {
            modelApi.push(modelView)
          }
        })
        const apiView = JSON.parse(JSON.stringify(view))
        apiView.apiList = modelApi
        if (modelApi.length !== 0) {
          this.apiList.push(apiView)
        }
      })
    },
    searchApiName() {
      this.apiList = []
      // 判断展示列表，如果输入了就展示没输入就不展示
      if (this.apiNameKeyword.length === 0) {
        this.apiList = JSON.parse(JSON.stringify(this.list))
        return
      }
      this.list.forEach(view => {
        const modelApi = []
        view.apiList.forEach(apiModelView => {
          const { apiName, apiList } = apiModelView
          if (apiName.indexOf(this.apiNameKeyword) !== -1) {
            const modelView = JSON.parse(JSON.stringify(apiModelView))
            modelView.apiList = apiList
            modelApi.push(modelView)
          }
        })
        const apiView = JSON.parse(JSON.stringify(view))
        apiView.apiList = modelApi
        if (modelApi.length !== 0) {
          this.apiList.push(apiView)
        }
      })
    },
    removeTab(t) {
      this.apiTabs.splice(t, 1)
      if (t > this.apiTabs.length - 1) {
        if (this.editableTabsValue === t) {
          this.editableTabsValue = (t - 1).toString()
        }
      } else {
        this.editableTabsValue = t.toString()
      }
    },
    triggerFile(event, row) {
      row.file = event.target.files[0]
    },
    downloadDoc(blob, filename) {
      var eleLink = document.createElement('a')
      eleLink.download = filename
      eleLink.style.display = 'none'
      // 字符内容转变成blob地址
      // var blob = new Blob(content)
      eleLink.href = URL.createObjectURL(blob)
      // 自动触发点击
      document.body.appendChild(eleLink)
      eleLink.click()
      // 然后移除
      document.body.removeChild(eleLink)
    },
    sendRequest(apiG, responseType) {
      this.dialogVisible = false
      const { method, path, requestType } = apiG.api
      let requestParams
      if (Object.prototype.toString.call(apiG.requestParams) === '[object Object]') {
        requestParams = apiG.requestParams
      } else {
        requestParams = formDataUtil.getJsonObj(apiG.requestParams)
      }
      if (method === 'GET') {
        getRequest(method, path, requestParams, responseType).then((res) => {
          const { data, headers } = res
          if (StringUtils.hasLength(responseType)) {
            this.downloadDoc(data, `${this.inputFileName}.${MimeTypeEnum.get(headers['content-type'].split(';')[0])}`)
          } else {
            apiG.data = data
          }
        })
      } else {
        let formData = null
        switch (requestType) {
          case 'FORM_DATA':
            formData = formDataUtil.getFormData2(requestParams)
            if (!StringUtils.isEmptyArray(apiG.fileData)) {
              apiG.fileData.forEach(fileD => {
                const { name, file, required } = fileD
                if (required) {
                  ErrorAssert.hasLength(file, '未选择文件，请选择后在操作！')
                }
                if (StringUtils.hasLength(file)) {
                  formData.append(name, file)
                }
              })
            }
            formDataRequest(method, path, formData, responseType).then((res) => {
              const { data, headers } = res
              if (StringUtils.hasLength(responseType)) {
                this.downloadDoc(data, `${this.inputFileName}.${MimeTypeEnum.get(headers['content-type'].split(';')[0])}`)
              } else {
                apiG.data = data
              }
            })
            break
          case 'JSON':
            jsonRequest(method, path, requestParams, responseType).then((res) => {
              const { data, headers } = res
              if (StringUtils.hasLength(responseType)) {
                this.downloadDoc(data, `${this.inputFileName}.${MimeTypeEnum.get(headers['content-type'].split(';')[0])}`)
              } else {
                apiG.data = data
              }
            })
            break
          case 'RESTFUL':
            restfulRequest(method, path, responseType).then((res) => {
              const { data, headers } = res
              if (StringUtils.hasLength(responseType)) {
                this.downloadDoc(data, `${this.inputFileName}.${MimeTypeEnum.get(headers['content-type'].split(';')[0])}`)
              } else {
                apiG.data = data
              }
            })
            break
          default:
            return ''
        }
      }
    },
    addFile(fileData, row) {
      const { name, describe, required } = row
      const fileDataParam = { name: name, array: false, describe, required }
      fileData.push(fileDataParam)
    },
    deleteFile(fileData, index, array) {
      ErrorAssert.isTrue(fileData.length > 1, '至少保留一个，不可删除')
      ErrorAssert.isTrue(!array, '不可删除这个')
      fileData.splice(index, 1)
    }
  }
}
</script>

<style scoped>

.api {
  width: 100%;
  display: flex;
  flex-direction: row;
}

.apiLeft {
  width: 20%;
}

.apiMenu {
  overflow:scroll;
  height: 90vh;
}

.apiRight {
  width: 80%;
}

.apiDocument{
  overflow:scroll;
  height: 90vh;
}

::v-deep  .el-menu-item {
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
  padding-left: 0;
}

.labelRight {
  display: flex;
  flex-direction: row;
  align-items: center;
  width: 100%;
}

.spanTitle {
  color: #ffffff;
  vertical-align: middle;
  display: table-cell;
  position: relative;
  border: 1px solid #dcdfe6;
  width: 80px;
  height: 38px;
  text-align: center;
  white-space: nowrap;
}

::v-deep .el-input input {
  height: 38px !important;
}
</style>
